/**
*	OMaster - Cheating Tool OGame
Door: Peter Evers
Datum: 28 November 2007

  Bestand: game.cpp
  Beschrijving:
  Dit bestand zal gebouwen accepteren en gaan bouwen.
**/

#include "game.h"

// Sleep function in millisecondes!!
void WaitConsole(double time)
{
#ifdef _WIN32
	Sleep((DWORD)time);
#else
	usleep(time * 1000);
#endif
}

void ClearConsole()
{
#ifdef _WIN32
	system("cls");
#else
	system("clear");
#endif
}

char *StringToUpper(char* szString)
{
	for(int i = 0; i < strlen(szString); i++)
		szString[i] = toupper(szString[i]);
	
	return szString;
}


/// STRUCTURE INFO	//
SBuilding buildings[NUMBER_OF_BUILDINGS] = { {1, 60, 15, 0, 1.5}, {2, 48, 24, 0, 1.6}, {3, 225, 75, 0, 1.5},
{4, 75, 30, 0, 1.5}, {14, 400, 120, 200, 2}, {22, 2000, 0, 0, 2},
{23, 2000, 1000, 0, 2}, {24, 2000, 2000, 0, 2}, {31, 200, 400, 200, 2},
{34, 20000, 40000, 0, 2}, {44, 20000, 20000, 1000, 2}};



// constructor
CGame::CGame() : m_bSpeedServer(false), m_tasksize(0), m_lang(NULL)
{
	
}

// destructor
CGame::~CGame()
{
	if(m_tasksize > 0)
		delete[] m_vectTask;
	if(m_lang != NULL)
		delete m_lang;
	m_tasksize = 0;
}

/**
* Voegt een gebouw toe
*
* @param    building        gebouw dat toegevoegd moet worden.
* @return   boolean         true als alles goed gaat.
*/
bool CGame::AddBuilding(int building, int planet)
{
    if((building >= NUMBER_OF_BUILDINGS) || building < 0)
        return false;
	if(planet > GetNumPlanets())
		return false;
	
	SBuilding sbuilding = buildings[building];
	sbuilding.planet = planet;
	sbuilding.userid = building;
	
    m_vectBuildings.insert(m_vectBuildings.begin(), sbuilding);
    return true;
}

/**
* Haalt gebouw weg
*
* @param       element                 gebouw dat weggehaald moet worden.
* @return      boolean                 true als alles goed gaat.
*/
bool CGame::RemoveBuilding(int element)
{
	if((element != 0) || (element > NUMBER_OF_BUILDINGS))
		return false;
	
	m_vectBuildings.erase(m_vectBuildings.begin() + element);   
	return true;
}

/**
* Login op de OGame server
* 
* @param	username		gebruikersnaam
* @param	password		wachtwoord
* @param	uni					uni, en ook host opgegeven dus, e.a. uni11.ogame.nl.
* @param	speed_server	geef true mee als er op een speed-server gespeeld wordt.
* @return CON_HOST, CON_WRONGLOGIN, CON_SESSION, CON_INTERERROR, CON_SUCCES. (TODO)
* 	Bij een gelukte login wordt de sessie en de uni opgeslagen m_szSession, m_szUni.
*/
int CGame::StartLogin(char *username, char *password, char *uni, bool speed_server)
{
	m_bSpeedServer = speed_server;
	
    /** Laad de taal! **/
    char *p = NULL;
    int iNumBuildings = 0;
    
    // Kijk naar het domein voor de taal!
    p = strstr(uni, ".");
	if(p == NULL)
		return CON_INTERERROR;
    p++;
    p = strstr(p, ".");
	if(p == NULL)
		return CON_INTERERROR;
    strcpy(m_szLanguage, (p + 1));   
    strcpy(m_szLanguage, StringToUpper(m_szLanguage));
	
    m_lang = new ConfigFile(LANGUAGE_FILE);
    if(!m_lang)
    {
#ifdef _DEBUG
        std::cout << "CGame::StartLogin - m_lang == NULL!" << std::endl;
#endif
        return CON_INTERERROR;
    }    
	
	// Post login-formulier
	if(!m_con.PostLoginForm(username, password, uni))
	{
#ifdef _DEBUG
		std::cout << "StartLogin: PostLoginForm returns false!" << std::endl;
#endif
		return CON_WRONGLOGIN;
	}
	
	// Haal de sessie op uit verkregen pagina
	strcpy(m_szUni, uni);
	char *session = m_parser.GetSession(m_con.GetLastPage());
	if(session == NULL)
		return CON_WRONGLOGIN;
	if(session)
		strcpy(m_szSession, session);
	else
	{
#ifdef _DEBUG
		std::cout << "StartLogin: session == NULL" << std::endl;
#endif
		return CON_SESSION;
	}
	
	// Laatste stap, ga naar de overzicht pagina!
	if(!m_con.GetPage(MENU_OVERVIEW, m_szSession, m_szUni, NULL))
	{
#ifdef _DEBUG
		std::cout << "StartLogin: GetPage returns false!" << std::endl;
#endif
		return CON_WRONGLOGIN;
	}		
	
	// Haal nu de planeetjes op!
	int planets = m_parser.GetPlanets(m_con.GetLastPage(), m_vectPlanets);
	if(planets == -1)
	{
#ifdef _DEBUG
		std::cout << "StartLogin: GetPlanets returns -1!" << std::endl;
#endif
		return CON_INTERERROR;
	}
	m_vectTask = new vector<STask>[planets];
	m_tasksize = planets;
	
	// Refresh voor toekomst calls!
	m_con.RefreshPage();
	
	m_bOnline = true;
	
	return CON_SUCCESS;
}

/**
* Haalt planeet op, de planeet op positie <planet> in de vector wordt teruggegeven.
*
* @param		planet	planeet die opgehaald moet worden.
* @return	SPlanet	structure met informatie over de planeet.
*/
SPlanet *CGame::GetPlanet(int planet)
{
	if((planet >= m_vectPlanets.size()) || (planet < 0))
		return NULL;
	return &m_vectPlanets[planet];
}

/**
* Berekent hoeveel metaal, kristal en deuterium geproduceerd wordt per seconde, resultaten in m_dMetaalProd, m_dKristalProd, m_dKristalProd.
* Ervan uitgaande dat er 100% energie is!! TODO: Max-: 67
*
* @param		planet		geeft aan van welke planeet productie berekent moet worden.
*/
bool CGame::CalcProduction(int planet)
{
	if(!m_con.GetPage(MENU_BUILDINGS, m_szSession, m_szUni, m_vectPlanets[planet].id.c_str()))
	{
#ifdef _DEBUG
		std::cout << "CalcProduction: GetPage returns false!" << std::endl;
#endif
		return false;
	}
	
	int level = m_parser.GetBuildingLevel(m_con.GetLastPage(), METAAL_MIJN, m_lang, m_szLanguage);
	
	m_dMetaalProd = (((floor(30 * level * pow(1.1, level))) *  ((m_bSpeedServer == true) ? 2 : 1)) + ((m_bSpeedServer == true) ? 40 : 20)) / 3600;
	level = m_parser.GetBuildingLevel(m_con.GetLastPage(), KRISTAL_MIJN, m_lang, m_szLanguage);
	m_dKristalProd = (((floor(20 * level * pow(1.1, level)) * ((m_bSpeedServer == true) ? 2 : 1))) + ((m_bSpeedServer == true) ? 20 : 10)) / 3600;
	level = m_parser.GetBuildingLevel(m_con.GetLastPage(), DEUT_MIJN, m_lang, m_szLanguage);
	m_dDeutProd = ((floor(10 * level * pow(1.1, level)) * (-0.002 * 68 + 1,28)) * ((m_bSpeedServer == true) ? 2 : 1)) / 3600;	// TODO: 68 naar planeet-temperatuur veranderen!!
	m_con.RefreshPage();
	
	return true;
}

/**
* Berekent hoeveel resources er nodig zijn om gebouw <building> te bouwen met level <level>
*
* @param		building		gebouw wat gebouwd moet worden.
* @param		level				level waarna het gebouw moet.
* @param		resources		hierin worden kosten opgeslagen.
*/
void CGame::CalcBuildCost(const int building, const int level, SResources &resources)
{
	resources.dMetaal = buildings[building].dMetaal * pow(buildings[building].factor, level-1);
	resources.dKristal = buildings[building].dKristal * pow(buildings[building].factor, level-1);
	resources.dDeut = buildings[building].dDeut * pow(buildings[building].factor, level-1);
}

/**
* Berekent hoeveel tijd er nodig is om gebouw <building> te bouwen met level <level>
*	TODO: NANO-NANO!!!! IMPORTANT!
* @param		building		gebouw wat gebouwd moet worden.
* @param		level				level waarna het gebouw moet.
* @param		planet			planeet waarop robot-fabriek staat.
* @return	double			tijd in seconden om het gebouw te bouwen.
*/
double CGame::CalcBuildTime(const int building, const int level, const int planet)
{
	if(!m_con.GetPage(MENU_BUILDINGS, m_szSession, m_szUni, m_vectPlanets[planet].id.c_str()))
	{
#ifdef _DEBUG
		std::cout << "CalcBuildTime: GetPage returns false!" << std::endl;
#endif
		return -1;
	}
	
	float robo = m_parser.GetBuildingLevel(m_con.GetLastPage(), ROBOT_FABRIEK, m_lang, m_szLanguage);
	if(robo == -1)
		return -1;
	m_con.RefreshPage();
	
	// Bereken hoeveel resources er nodig zijn
	SResources res_new;
	CalcBuildCost(building, level, res_new);
	return (((res_new.dMetaal + res_new.dKristal) / (2500.0 * (robo + 1.0))) * ((m_bSpeedServer == true) ? 0.5 : 1)) * 3600;
}

/**
* Maakt een nieuwe taak.
* 
* @return	TASK_SUCCESS, TASK_END_LIST, TASK_CON_ERROR
*/
int CGame::AddTask()
{	
    string strTemp;
	if(m_vectBuildings.size() == 0)
		return TASK_END_LIST;
	
	// Voor elke planeet task-lijst bouwen!
	std::vector<SBuilding>::reverse_iterator it;
	for(it = m_vectBuildings.rbegin(); it < m_vectBuildings.rend(); it++)
	{
		int building = (*it).userid;
		int planet = (*it).planet;
		
		if(m_vectTask[planet].size() > 0)	// Controleer of hier al gebouwd wordt
			continue;
		
		int level;
		STask task;
		SResources res, res_needed;	
		
		// Verwijder oude pagina!
		m_con.RefreshPage();
		
		// Haal grondstoffen op
		if(!m_con.GetPage(MENU_OVERVIEW, m_szSession, m_szUni, m_vectPlanets[planet].id.c_str()))
			return TASK_CON_ERROR;
		if(!m_parser.GetResources(m_con.GetLastPage(), res))
			return TASK_CON_ERROR;
		m_con.RefreshPage();
		
		if(!m_con.GetPage(MENU_BUILDINGS, m_szSession, m_szUni, m_vectPlanets[planet].id.c_str()))
			return TASK_CON_ERROR;
		level = m_parser.GetBuildingLevel(m_con.GetLastPage(), building, m_lang, m_szLanguage);
		if(level == -1)
			return TASK_CON_ERROR;
		m_con.RefreshPage();
		
		level += 1;
		CalcBuildCost(building, level, res_needed);
		
		// Check of er genoeg resources zijn!
		FLAG res_short = 0;
		if(res_needed.dMetaal > res.dMetaal)
			res_short |= RES_SHORT_METAAL;
		if(res_needed.dKristal > res.dKristal)
			res_short |= RES_SHORT_KRISTAL;
		if(res_needed.dDeut > res.dDeut)
			res_short |= RES_SHORT_DEUT;
		
		if(res_short != 0)
		{
			// Niet genoeg grondstoffen, bereken hoe lang het duurt voordat er genoeg grondstoffen zijn
			if(!CalcProduction(planet))
				return TASK_CON_ERROR;
			
			double dtime = 0, dtemp = 0;
			if(res_short & RES_SHORT_METAAL)
				dtime = (res_needed.dMetaal - res.dMetaal) / m_dMetaalProd;
			if(res_short & RES_SHORT_KRISTAL)
			{
				dtemp = (res_needed.dKristal - res.dKristal) / m_dKristalProd;
				if(dtemp > dtime)
					dtime = dtemp;
			}
			if(res_short & RES_SHORT_DEUT)
			{
				dtemp = (res_needed.dDeut - res.dDeut) / m_dDeutProd;
				if(dtemp > dtime)
					dtime = dtemp;
			}	
			task.type = TASK_WAIT_RESOURCES;
			task.time = (time_t)dtime;
			task.id = buildings[building].id;
			task.building = building;
			task.planet = planet;
			
			if(planet > m_tasksize)
				return TASK_SIZE_ERROR;
			m_vectTask[planet].push_back(task);
		}
		else
		{
			// Bereken wanneer dit gebouw klaar is!
			task.time = (time_t)CalcBuildTime(building, level, planet);
			if(task.time == -1)
				return TASK_CON_ERROR;
			
			// Gebouw kan gebouwd worden
			task.type = TASK_BUILD_BUILDING;
			task.id = buildings[building].id;
			task.building = building;
			task.planet = planet;
			
			// Voer bouw-script uit
			std::stringstream out;
			out << task.building;
			strTemp = out.str();
			std::cout << "-|OMaster|- : Start bouwen van: " << (string)m_lang->Value(m_szLanguage, strTemp) << " op planeet: " << m_vectPlanets[task.planet].name << std::endl;
			if(!m_con.BuildBuilding(task.id, m_szSession, m_szUni, m_vectPlanets[planet].id.c_str()))
				return TASK_CON_ERROR;
			
			if(planet > m_tasksize)
				return TASK_SIZE_ERROR;
			m_vectTask[planet].push_back(task);			
		}
	}	
	return TASK_SUCCESS;
}		

/**
* Controleert of er task klaar zijn en start zonodig een nieuwe!
*
* @return	TASK_xxx.
*/
int CGame::ControlTask()
{
	int ret = TASK_END_LIST;
	STask task;
	std::stringstream out;
	string strTemp;
	
	for(int i = 0; i < m_tasksize; i++)
	{
		if(m_vectTask[i].size() == 0)
			continue;
		std::vector<STask>::iterator it = m_vectTask[i].begin();
		task = *it;
		
		static time_t finish = time(NULL) + task.time;
		
		if(finish < time(NULL))
		{
			out << task.building;
            strTemp = out.str();
			if(task.type == TASK_WAIT_RESOURCES)
			{
				
				std::cout << "----------------------" << std::endl;
				std::cout << "-|OMaster|- : Genoeg resources voor: " << (string)m_lang->Value(m_szLanguage, strTemp) << " op planeet " << m_vectPlanets[task.planet].name << std::endl;
				std::cout << "----------------------" << std::endl;
				
				m_vectTask[i].clear();
				ret = AddTask();
			}
			else if(task.type == TASK_BUILD_BUILDING)
			{
				std::cout << "----------------------" << std::endl;
				std::cout << "-|OMaster|- : Klaar met bouwen van " << (string)m_lang->Value(m_szLanguage, strTemp)<< " op planeet " << m_vectPlanets[task.planet].name << std::endl;
				std::cout << "----------------------" << std::endl;
				
				m_vectTask[i].clear();
				m_vectBuildings.erase(m_vectBuildings.end()-1);
				ret = AddTask();

			}
			out.str("");
		}
		else
			ret = TASK_SUCCESS;
	}
	return ret;
}

/**
* Geeft het aantal planeten terug die de speler bezit.
*/
int CGame::GetNumPlanets()
{
	return m_vectPlanets.size();
}

/**
* START de builder!
*
* @return   integer     RUN_SUCCESS, RUN_FAILURE, RUN_SESSION.
*/
int CGame::RunBuilder()
{	
	if(!m_bOnline)
		return RUN_FAILURE;
	string strTemp;
	std::stringstream out;

	// Clear Console //
	ClearConsole();

	AddTask();	// Voeg aankomende taken toe!	

	while(ControlTask() != TASK_END_LIST)
	{
		time_t temp = 0, shortest = 0;
		STask task;
		for(int i = 0; i < m_tasksize; i++)
		{
			if(m_vectTask[i].size() == 0)
				continue;			
			vector<STask>::iterator it = m_vectTask[i].end() - 1;
			task = *it;
			out << task.building;
			strTemp = out.str();

			if((task.time < shortest) || (shortest == 0))
				shortest = task.time;

			temp = task.time + time(NULL);

			std::cout << "----------------------" << std::endl;
			std::cout << "-|OMaster|- : Planeet " << m_vectPlanets[i].name << std::endl;
			if(task.type == TASK_WAIT_RESOURCES)
				std::cout << "-|OMaster|- : wachten op genoeg resource voor " << (string)m_lang->Value(m_szLanguage, strTemp) << " tot " << asctime(localtime(&temp)) << std::endl;
			else if(task.type == TASK_BUILD_BUILDING)
				std::cout << "-|OMaster|- : Wachten tot gebouw " << (string)m_lang->Value(m_szLanguage, strTemp) << " klaar is tot " << asctime(localtime(&temp)) << std::endl;				
			std::cout << "----------------------" << std::endl;
			out.str("");
		}
		temp = shortest + time(NULL);
		std::cout << "-|OMaster|- : Eerst volgende achtie om: " << asctime(localtime(&temp)) << std::endl;
		WaitConsole(shortest * 1000 + 5 * 1000);
		shortest = 0;
	}
	std::cout << "-|OMaster|- : Klaar!" << std::endl;
	return RUN_SUCCESS;
}



